import { _decorator, Component, Node } from 'cc';
import { AutoAimSystem } from './AutoAimSystem';
import { AutoReloadSystem } from './AutoReloadSystem';
import { Clock } from '../core/Clock';
import { TICK_RATES } from '../shared';
import { EnemyRegistry } from '../gameplay/EnemyRegistry';
import { Ballistics } from '../combat/Ballistics';
import { Random } from '../math/Random';
import { GlobalEventBus } from '../core/EventBus';
import WeaponController from '../gameplay/WeaponController';

const { ccclass, property } = _decorator;

@ccclass('AutoFireSystem')
export class AutoFireSystem extends Component {
  @property(Clock)
  public clock: Clock | null = null;

  @property(AutoAimSystem)
  public autoAim: AutoAimSystem | null = null;

  @property(AutoReloadSystem)
  public autoReload: AutoReloadSystem | null = null;

  @property(Node)
  public muzzle: Node | null = null;

  private rofInterval = 0.2;
  private accumulator = 0; // kept for legacy but not used for gating
  private clockKey = '';
  private random = new Random(Date.now());
  private currentWeaponId: string | null = null;
  private lastShotAt = 0;
  private weaponController: WeaponController | null = null;
  private lastSkipReason: string | null = null;

  onEnable() {
    this.weaponController = this.node.getComponent(WeaponController);
    this.recalculateRof();
    this.clockKey = `auto-fire-${this.node.uuid}`;
    this.ensureRegistered();
  }

  onDisable() {
    this.clock?.unregister(this.clockKey);
    this.unscheduleAllCallbacks();
  }

  /** 显式绑定时钟并注册到渲染频率 */
  public bindClock(clock: Clock) {
    this.clock = clock;
    this.ensureRegistered();
  }

  private ensureRegistered() {
    if (!this.clock) return;
    (this.clock as any).unregister?.(this.clockKey);
    this.clock.register(this.clockKey, TICK_RATES.render, (dt) => this.onTick(dt));
  }

  public recalculateRof() {
    const wc = this.weaponController ?? this.node.getComponent(WeaponController);
    if (!wc) {
      return;
    }
    const cur = wc.getCurrent();
    this.currentWeaponId = cur.id;
    const rps = Math.max(cur.rof, 0.01);
    this.rofInterval = 1 / rps; // cooldown = 1/rof
  }

  private onTick(dt: number) {
    if (!this.autoAim || !this.autoReload || !this.muzzle) {
      return;
    }
    if (this.autoReload.isCeaseFire()) {
      if (typeof console !== 'undefined' && console.debug) {
        console.debug('[AFIRE] skip: cease fire');
      }
      return;
    }
    // If reloading or empty, skip with unified log
    const wc = this.weaponController;
    if (!wc) return;
    // Update cooldown based on current ROF each tick
    const curState = wc.getCurrent();
    const isReloading = this.autoReload.isReloading();
    const rps = Math.max(curState.rof, 0.01);
    this.rofInterval = 1 / rps;
    if (isReloading || curState.mag <= 0) {
      this.logSkipOnce('reloading/empty');
      return;
    }

    const now = performance.now() / 1000;
    if (now - this.lastShotAt < this.rofInterval) {
      this.logSkipOnce('cooldown');
      return;
    }

    const targetId = this.autoAim.getTargetId();
    if (!targetId) {
      this.logSkipOnce('no-target');
      return;
    }
    const weapon = this.autoReload.getActiveWeaponStats();
    if (!weapon) {
      return;
    }
    const wcCur = wc.getCurrent();
    if (wcCur.id !== this.currentWeaponId) {
      this.recalculateRof();
    }

    const enemy = EnemyRegistry.get(targetId);
    if (!enemy || !enemy.node.isValid) {
      return;
    }

    // SSOT: WeaponController manages ammo; fire first, then consume regardless of hit
    const hits = Ballistics.fire({
      origin: this.muzzle.worldPosition.clone(),
      targetId,
      targetPosition: enemy.position,
      weapon,
      speedNorm: this.autoReload.getSpeedNorm(),
      laserAssist: weapon.attachments.laserAssist,
      random: this.random,
    });
    this.lastShotAt = now;
    // Reset skip reason upon actual firing so next skip can log again
    this.lastSkipReason = null;

    for (const hit of hits) {
      GlobalEventBus.emit('combat/hit', {
        src: this.node.uuid,
        dst: hit.dstId,
        dmg: hit.dmg,
        isCrit: hit.isCrit,
        pierceLeft: hit.pierceLeft,
      });
      if (typeof console !== 'undefined' && console.debug) {
        console.debug('[HIT]', { dst: hit.dstId, dmg: hit.dmg, crit: hit.isCrit, pierceLeft: hit.pierceLeft });
      }
    }
    // After shot attempt, consume one round always (hit or miss)
    this.weaponController?.consumeOneRound();
    const after = this.weaponController?.getCurrent();
    if (typeof console !== 'undefined' && console.debug && after) {
      console.debug('[AFIRE] shot -> mag:', after.mag, 'reserve:', after.reserve);
    }
  }

  private logSkipOnce(reason: string) {
    if (this.lastSkipReason === reason) return;
    this.lastSkipReason = reason;
    if (typeof console !== 'undefined' && console.debug) {
      if (reason === 'reloading/empty') {
        console.debug('[AFIRE] skip (reloading/empty)');
      } else if (reason === 'no-target') {
        console.debug('[AFIRE] skip: no target');
      } else if (reason === 'cooldown') {
        console.debug('[AFIRE] skip: cooldown');
      } else {
        console.debug('[AFIRE] skip:', reason);
      }
    }
  }
}

